package heap

import (
	"fmt"
	"jvm/nzpJvm/classfile"
	"jvm/nzpJvm/classpath"
	"jvm/nzpJvm/runtime_area/heap/utils"
)

type ClassLoader struct {
	classPaths *classpath.Classpath
	classMap   map[string]*Class // 由这个类加载器加载的类
}

// 新建一个类加载器
func NewClassLoader(classPaths *classpath.Classpath) *ClassLoader {
	loader := &ClassLoader{
		classPaths: classPaths,
		classMap:   make(map[string]*Class),
	}
	loader.LoadBasicClass()
	loader.LoadPrimitiveClasses()
	return loader

}

// 如果一个类已经加载过，则直接返回，否则尝试加载这个类
func (self *ClassLoader) LoadClass(name string) *Class {
	if class, ok := self.classMap[name]; ok {
		return class
	}
	// 数组类
	var class *Class
	if name[0] == '[' {
		class = self.LoadArrayClass(name)
	} else {
		class = self.loadNonArrayClass(name)
	}
	self.generateJavaClassObject(class)
	return class

}

// 为加载到的类型生成Class类型的对象
func (self *ClassLoader) generateJavaClassObject(class *Class) {
	// 已经加载过Class类才创建。如果还没加载过Class类，就不创建，而是等到加载了Class类之后去创建
	if javaClassClass, ok := self.classMap["java/lang/Class"]; ok {
		// 将元空间中Class结构体和堆中Class对象关联起来
		class.javaClassObject = javaClassClass.NewObject()
		class.javaClassObject.extra = class
	}
}

// 加载非数组类Class对象
func (self *ClassLoader) loadNonArrayClass(name string) *Class {
	// 获取class文件的二进制流
	data, entry := self.readClass(name)
	class := self.defineClass(data) // 根据二进制流定义Class对象
	// 链接
	link(class)
	fmt.Printf("load class [%s] from [%s]\n", name, entry)
	return class
}

func link(class *Class) {
	// 验证
	verify(class)
	// 准备
	prepare(class)
	// 解析。将符号引用解析成直接引用
	// jvm规范中将解析符号引用描述在链接阶段中
	// 但是此jvm实现将解析符号引用延迟到实际使用时再解析
	// todo
}

func verify(class *Class) {
	// todo
}

// 准备阶段
func prepare(class *Class) {
	// 计算这个类的实例变量个数和所占的空间，以及每一个实例变量的id
	calcInstanceFieldsSlotIds(class)
	// 计算这个类的静态变量个数和所占的空间，以及每一个静态变量的id
	calcStaticFieldsSlotIds(class)
	// 为静态变量分配空间并且设置初始值
	allocAndInitStaticVars(class)
}

// 计算每一个实例变量的id
func calcInstanceFieldsSlotIds(class *Class) {
	slotId := uint(0)
	if class.superClass != nil {
		// 实例变量个数。意思是计数的时候从父类开始计数.这个数量并不一定等于实例字段的数量
		slotId = class.superClass.instanceSlotCount
	}
	for _, field := range class.fields {
		if !field.IsStatic() {
			field.slotId = slotId
			slotId++
			// lang和double占两个位置
			if field.isLongOrDouble() {
				slotId++
			}
		}
	}
	class.instanceSlotCount = slotId

}

// 计算每一个静态变量的id
func calcStaticFieldsSlotIds(class *Class) {
	slotId := uint(0)

	for _, field := range class.fields {
		if field.IsStatic() {
			field.slotId = slotId
			slotId++
			// lang和double占两个位置
			if field.isLongOrDouble() {
				slotId++
			}
		}
	}
	class.staticSlotCount = slotId
}

// 为静态变量分配空间，如果是static final的常量，则直接赋值
func allocAndInitStaticVars(class *Class) {
	// 这个就相当于是分配空间
	class.staticVars = newSlots(class.staticSlotCount)
	for _, field := range class.fields { // 如果是static final变量，则直接赋值
		if field.IsStatic() && field.IsFinal() {
			// 对static final变量赋值。如果字段是基本类型或者字符串，则意味着是常量，其值保存在常量池中
			// 如果是其他引用类型，比如Date类型，则那个字段的初始化工作在调用<clinit>初始化方法时进行，此方法不处理
			initStaticFinalVar(class, field)
		}
	}
}

// 初始化static final常量的值
func initStaticFinalVar(class *Class, field *Field) {
	vars := class.staticVars
	constantPool := class.constantPool
	// 从常量池中取出
	cpIndex := field.ConstValueIndex()
	slotId := field.slotId
	if cpIndex > 0 {
		// 从常量池中获取常量
		constant := constantPool.GetConstant(cpIndex)

		switch field.Descriptor() {
		case "Z", "B", "C", "S", "I":
			// boolean，byte，char，short，int都当做int来处理
			val := constant.(int32)
			vars.SetInt(slotId, val)
		case "J":
			// long
			val := constant.(int64)
			vars.SetLong(slotId, val)
		case "F":
			// float
			val := constant.(float32)
			vars.SetFloat(slotId, val)
		case "D":
			// double
			val := constant.(float64)
			vars.SetDouble(slotId, val)
		case "Ljava/lang/String":
			// 字符串常量
			val := constant.(string)
			javaString := JavaString(class.loader, val)
			vars.SetRef(slotId, javaString)
		}
	}
}

// 读取类的二进制字节流
func (self *ClassLoader) readClass(name string) ([]byte, classpath.Entry) {
	classData, entry, err := self.classPaths.ReadClass(name)
	if err != nil {
		panic("java.lang.ClassNotFoundException")
	}
	return classData, entry
}

func (self *ClassLoader) defineClass(data []byte) *Class {
	class := parseClass(data)
	class.loader = self
	// 先加载这个类的父类
	resolveSuperClass(class)
	resolverInterfaces(class)
	self.classMap[class.name] = class // 在类加载器中保留这个类的指针，其实也就相当于标记这个类已经被加载过
	return class
}

// 数组类的Class对象是jvm自己创建的
func (self *ClassLoader) LoadArrayClass(name string) *Class {
	class := &Class{
		// 访问标志设置为public
		accessFlags: ACC_PUBLIC,
		// 类的名称
		name: name,
		// 加载这个类的类加载器
		loader: self,
		// 已经初始化
		initStarted: true,
		// 超类是Object类
		superClass: self.LoadClass("java/lang/Object"),
		// 实现了克隆和序列化接口
		interfaces: []*Class{
			self.LoadClass("java/lang/Cloneable"),
			self.LoadClass("java/io/Serializable"),
		},
	}
	// 标记为已加载
	self.classMap[name] = class
	return class
}

func (self *ClassLoader) LoadBasicClass() {
	// 首先加载Class类，为每个类生成一个Class对象
	javaClassClass := self.LoadClass("java/lang/Class")
	// 加载完Class类之后，立刻为已经加载的所有类生成Class对象。
	//这可以保证在Class类加载前加载的类（例如Object）也能生成Class对象
	for _, class := range self.classMap {
		if class.javaClassObject == nil {
			class.javaClassObject = javaClassClass.NewObject()
			class.javaClassObject.extra = class

		}
	}
}

func (self *ClassLoader) LoadPrimitiveClasses() {
	// 遍历所有基本类型和void，为其生成Class结构体和Class对象
	for primiryType := range utils.PrimitiveTypes {
		self.loadPrimitivrClass(primiryType)
	}
}

// 为基本类型对象生成CLass对象
func (self *ClassLoader) loadPrimitivrClass(className string) {
	class := &Class{
		accessFlags: ACC_PUBLIC,
		name:        className,
		loader:      self,
		initStarted: true,
		// java代码
		// Class<Integer> intClass = int.class;
		// System.out.println(intClass.getSuperclass()); => 输出为null

		// todo 基本类型竟然没有继承Object类？
		//superClass: self.LoadClass("java/lang/Object"),
	}
	self.generateJavaClassObject(class)
	self.classMap[className] = class
}

// 加载实现的接口的class对象
func resolverInterfaces(class *Class) {
	interfaces := make([]*Class, len(class.interfaces))
	for _, interfaceName := range class.interfaceNames {
		// 加载实现的接口
		interfaces = append(interfaces, class.loader.LoadClass(interfaceName))
	}
	class.interfaces = interfaces
}

// 加载父类
func resolveSuperClass(class *Class) {
	if class.name != "java/lang/Object" {
		// 除了Object类之外，其他类都有父类。先加载他的父类
		class.superClass = class.loader.LoadClass(class.superClassName)
	}
}

// 将class文件的二进制字节流解析成Class结构体
func parseClass(data []byte) *Class {
	// 解析class文件，得到classFile结构体
	classFile, err := classfile.Parse(data)
	if err != nil {
		panic("class format error")
	}
	// 根据class文件创建Class对象
	return newClass(classFile)
}
